﻿using BattleBotsMattAndSunny.Components.Interfaces;
using BattleBotsMattAndSunny.Utilities;
using Robocode;
using System;

namespace BattleBotsMattAndSunny.Components.Guns
{
    public class LinearGunner : IGunManager
    {
        private AdvancedRobot robot { get; set; }

        private IDataManager dataManager { get; set; }

        private double DegreesRightRemaining { get; set; }

        public LinearGunner(IDataManager dm, AdvancedRobot r)
        {
            dataManager = dm;
            robot = r;
        }

        public void OnScannedRobot(ScannedRobotEvent e, double power)
        {
            var enemy = dataManager.GetEnemy(e.Name);
            var bulletV = Rules.GetBulletSpeed(power);
            //Angle is
            //arcsine((LV * t) / (bulletVelocity * t))
            //Where LV = lateral velocity
            //t = time till impact
            //Need to solve for t's first
            //Then choose shortest t
            //Then calculate final X & Y from this shortest t
            //Then calculate rotation angle

            var A = (enemy.X - robot.X) / bulletV;
            var B = enemy.Velocity / bulletV * Math.Sin(enemy.HeadingRadians);
            var C = (enemy.Y - robot.Y) / bulletV;
            var D = enemy.Velocity / bulletV * Math.Cos(enemy.HeadingRadians);

            var a = A * A + C * C;
            var b = 2 * (A * B + C * D);
            var c = (B * B + D * D - 1);
            var discrim = b * b - 4 * a * c;
            if (!(discrim >= 0)) return; //There is no solution for quadratic
            // Reciprocal of quadratic formula
            var t1 = 2 * a / (-b - Math.Sqrt(discrim));
            var t2 = 2 * a / (-b + Math.Sqrt(discrim));

            double? alt1, alt2;
            RoboMath.Quadratic(a, b, c, out alt1, out alt2);

            var t = Math.Min(t1, t2) >= 0 ? Math.Min(t1, t2) : Math.Max(t1, t2);
            // Assume enemy stops at walls
            var endX = limit(
                enemy.X + enemy.Velocity * t * Math.Sin(enemy.HeadingRadians),
                robot.Width / 2, robot.BattleFieldWidth - robot.Width / 2);
            var endY = limit(
                enemy.Y + enemy.Velocity * t * Math.Cos(enemy.HeadingRadians),
                robot.Height / 2, robot.BattleFieldHeight - robot.Height / 2);
            //Sub the final angle and choose
            DegreesRightRemaining = CommonActions.AngleToPoint(endX, endY, robot);
            robot.TurnGunRightRadians(DegreesRightRemaining);
            Fire(power);
        }

        private double limit(double value, double min, double max)
        {
            return Math.Min(max, Math.Max(min, value));
        }

        public void Fire(double power)
        {
            //Fire and store the bullet todo move this to abstract GunManager class
            dataManager.StoreBullet(robot.SetFireBullet(power));
        }

        public void Move()
        {
        }
    }
}